package cn.org.tpeach.nosql.tools;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author tyz
 * @Title: ReflectUtil
 * @ProjectName Redis-Lettuce-Parent
 * @Description:
 * @date 2020-06-09 11:42
 * @since 1.0.0
 */
public class ReflectUtil {
    /**
     * 拼接某属性的 get/set 方法
     *
     * @param field
     * @return String
     */
    private static String parName(Field field, String prefix) {
        if (null == field) {
            return null;
        }
        if(StringUtils.equalsIgnoreCase("serialVersionUID",field.getName())){
            return null;
        }
        String fieldName = field.getName();
        if (StringUtils.equalsIgnoreCase("get",prefix) && field.getGenericType().toString().equals("boolean") ) {
            if(fieldName.startsWith("is")){
                return fieldName;
            } else{
                prefix = "is";
            }
        }
//        int startIndex = 0;
//        //第二个单词大写时,第一个单词不变
//        String first = null;
//        if (fieldName.length() > 1 && Character.isUpperCase(fieldName.charAt(1))) {
//            first = fieldName.substring(startIndex, startIndex + 1);
//        } else {
//            first = fieldName.substring(startIndex, startIndex + 1).toUpperCase();
//        }
//
//        return prefix + first + fieldName.substring(startIndex + 1);
        char[] chars = fieldName.toCharArray();
        chars[0] = (char)(chars[0] - 32);
        return prefix + new String(chars);

    }

    public static String parGetName(Field field) {
        return parName(field, "get");
    }

    public static String parSetName(Field field) {
        return parName(field, "set");
    }

    public static <T> T mapToObject(Map<String, ?> map, Class<T> clazz) throws Exception {
        if (DataStructureUtils.isEmpty(map)){
            return null;
        }

        T obj = clazz.newInstance();
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            Method setter = property.getWriteMethod();
            if (setter != null) {
                Class<?> propertyType = property.getPropertyType();
                Object o = map.get(property.getName());
                if(o != null){

                    if(propertyType.equals(Integer.class)){
                        setter.invoke(obj,Integer.valueOf(o.toString()));
                    }else if(propertyType.equals(Double.class)){
                        setter.invoke(obj,Double.valueOf(o.toString()));
                    }else if(propertyType.equals(Long.class)){
                        setter.invoke(obj,Long.valueOf(o.toString()));
                    }else if(propertyType.equals(Float.class)){
                        setter.invoke(obj,Float.valueOf(o.toString()));
                    }else if(propertyType.equals(Short.class)){
                        setter.invoke(obj,Short.valueOf(o.toString()));
                    }else if(propertyType.equals(String.class)){
                        setter.invoke(obj,o.toString());
                    }else{
                        setter.invoke(obj,o);
                    }

                }
            }
        }
        return obj;
    }



    public static void copyProperties(Object source, Object target) {
        //需要设置的数据
        if(source == null || target == null){
            return;
        }
        Field[] fields = target.getClass().getDeclaredFields();
        if(DataStructureUtils.isNotEmpty(fields)){
            List<Field> fieldList = Stream.of(fields).collect(Collectors.toList());
            Field[] sourceFields = source.getClass().getDeclaredFields();
            if(DataStructureUtils.isNotEmpty(sourceFields)){
                for (Field sourceField : sourceFields) {
                    for (Field field : fieldList) {
                        if(sourceField.getName().equals(field.getName())){
                            //get
                            Object value = getValue(sourceField, source);
                            //set
                            setValue(target,field,value);
                            fieldList.remove(field);
                            break;
                        }
                    }
                }
            }
        }
    }


    public static Object getValue(Field field, Object obj) {
        String methodName = parGetName(field);
        try {
            if(StringUtils.isNotEmpty(methodName)){
                Class<?> c = obj.getClass();
                Method method = c.getMethod(methodName);
                return method.invoke(obj);
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 设置bean的某个属性值
    public static void setValue(Object obj, Field field, Object value)  {
        try {
            String methodName = parSetName(field);
            if (StringUtils.isNotEmpty(methodName)) {
                Class<?> c = obj.getClass();
                Method method = c.getDeclaredMethod(methodName,new Class[]{field.getType()});
                method.invoke(obj,new Object[]{value});
            }
        }catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
    }

    // 获取bean的某个属性值
    public static Object getProperty(Object obj, String fieldName) throws Exception {
        // 获取Bean的某个属性的描述符
        PropertyDescriptor proDescriptor = new PropertyDescriptor(fieldName, obj.getClass());
        // 获得用于读取属性值的方法
        Method methodGetUserName = proDescriptor.getReadMethod();
        // 读取属性值
        Object value = methodGetUserName.invoke(obj);
        return value;
    }
    public static <T> T nullToNewObj(T object,Class<T> clazz){
        if(Objects.isNull(object)){
            try {
                return clazz.getDeclaredConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }
        return object;
    }

}
